﻿using Microsoft.Extensions.DependencyInjection;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ClassLibrary1
{
    public static class RabbitMqConsumerRegister
    {
        public static void Register(IServiceProvider serviceProvider)
        {
            var assembly = typeof(RabbitMqConsumerRegister).Assembly;
            var types = assembly.GetTypes();
            var dict = types.GetClassTypeAndGenericTypeRealType(typeof(IRabbitMqMessageConsumer<>));
            var rabbitMqConnectionFactory = serviceProvider.GetService<IRabbitMqConnectionFactory>();

            foreach (var item in dict)
            {
                var channel = rabbitMqConnectionFactory!.Connection.CreateModel();
                var attr = item.Value.GetCustomAttribute<AttAttribute>(inherit: true);
                if (attr == null)
                {
                    continue;
                }
                channel.ExchangeDeclare(exchange: attr!.ExchangeName,
                    type: attr!.ExchangeType,
                    durable: false,
                    autoDelete: false,
                    arguments: null
                );
                channel.QueueDeclare(
                queue: attr!.QueueName,
                durable: true,
                exclusive: false,
                autoDelete: false,
                arguments: null
                );

                channel.QueueBind(queue: attr!.QueueName, exchange: attr!.ExchangeName, routingKey: attr!.RoutingKey, arguments: null);
                channel.BasicQos(prefetchSize: 0, prefetchCount: 1, global: false);
                var consumer = new EventingBasicConsumer(channel);
                var rabbitMqConsumer = Activator.CreateInstance(item.Key);
                var consumeMethod = item.Key.GetMethod("Consume");
                var paramterType = item.Value;
                consumer.Received += (sender, e) =>
                {
                    var message = System.Text.Encoding.UTF8.GetString(e.Body.ToArray());
                    var obj = Newtonsoft.Json.JsonConvert.DeserializeObject(message, paramterType);
                    try
                    {
                        consumeMethod!.Invoke(rabbitMqConsumer, new object[] { obj });
                    }
                    catch (Exception)
                    {
                        consumer.Model.BasicReject(e.DeliveryTag, requeue: true);
                    }
                    consumer.Model.BasicAck(e.DeliveryTag, multiple: false);
                };

                channel.BasicConsume(queue: attr!.QueueName, autoAck: false, consumer);
            }

        }

        private static Dictionary<Type, Type> GetClassTypeAndGenericTypeRealType(this Type[] types, Type genericType)
        {
            Dictionary<Type, Type> dict = new Dictionary<Type, Type>();
            foreach (Type item in types)
            {
                if (item.IsInterface) continue;//判断是否是接口
                Type[] ins = item.GetInterfaces();
                foreach (Type ty in ins)
                {
                    if (!ty.IsGenericType) continue;
                    if (ty.GetGenericTypeDefinition() == genericType.GetGenericTypeDefinition())
                    {
                        var argumentTypes = ty.GenericTypeArguments;
                        dict.Add(item, argumentTypes[0]);
                    }
                }
            }
            return dict;
        }
    }
}
